using System.Diagnostics;
using System.Collections.Generic;

//
// * Copyright (c) 2002-2009 "Neo Technology,"
// *     Network Engine for Objects in Lund AB [http://neotechnology.com]
// *
// * This file is part of Neo4j.
// * 
// * Neo4j is free software: you can redistribute it and/or modify
// * it under the terms of the GNU Affero General Public License as
// * published by the Free Software Foundation, either version 3 of the
// * License, or (at your option) any later version.
// * 
// * This program is distributed in the hope that it will be useful,
// * but WITHOUT ANY WARRANTY; without even the implied warranty of
// * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// * GNU Affero General Public License for more details.
// * 
// * You should have received a copy of the GNU Affero General Public License
// * along with this program. If not, see <http://www.gnu.org/licenses/>.
// 
namespace org.neo4j.kernel.impl.nioneo.store
{


	using Test = junit.framework.Test;
	using TestCase = junit.framework.TestCase;
	using TestSuite = junit.framework.TestSuite;

	using AbstractDynamicStore = org.neo4j.kernel.impl.nioneo.store.AbstractDynamicStore;
	using CommonAbstractStore = org.neo4j.kernel.impl.nioneo.store.CommonAbstractStore;
	using DynamicRecord = org.neo4j.kernel.impl.nioneo.store.DynamicRecord;

	public class TestDynamicStore : TestCase
	{

		public TestDynamicStore(string testName) : base(testName)
		{
		}

		static void Main(string[] args)
		{
			junit.textui.TestRunner.run(suite());
		}

		public static Test suite()
		{
			TestSuite suite = new TestSuite(typeof(TestDynamicStore));
			return suite;
		}

		public virtual void setUp()
		{
		}

		public virtual void tearDown()
		{
		}

		public virtual void testCreateStore()
		{
			try
			{
				try
				{
					ByteStore.createStore(null, 1);
					fail("Null fileName should throw exception");
				}
				catch (IllegalArgumentException e)
				{ // good
				}
				try
				{
					ByteStore.createStore("testDynamicStore.db", 0);
					fail("Illegal blocksize should throw exception");
				}
				catch (IllegalArgumentException e)
				{ // good
				}
				ByteStore store = ByteStore.createStore("testDynamicStore.db", 30);
				try
				{
					ByteStore.createStore("testDynamicStore.db", 15);
					fail("Creating existing store should throw exception");
				}
				catch (IllegalStateException e)
				{ // good
				}
				store.Close();
			}
			finally
			{
				File file = new File("testDynamicStore.db");
				if (file.exists())
				{
					assertTrue(file.delete());
				}
				file = new File("testDynamicStore.db.id");
				if (file.exists())
				{
					assertTrue(file.delete());
				}
			}
		}

		public virtual void testStickyStore()
		{
			Logger log = Logger.getLogger(typeof(CommonAbstractStore).Name);
			Level level = log.getLevel();
			try
			{
				log.setLevel(Level.OFF);
				ByteStore.createStore("testDynamicStore.db", 30).Close();
				java.nio.channels.FileChannel fileChannel = new java.io.RandomAccessFile("testDynamicStore.db", "rw").getChannel();
				fileChannel.truncate(fileChannel.size() - 2);
				fileChannel.Close();
				ByteStore store = new ByteStore("testDynamicStore.db");
				store.makeStoreOk();
				store.Close();
			}
			catch (IOException e)
			{
				fail("" + e);
			}
			finally
			{
				log.setLevel(level);
				File file = new File("testDynamicStore.db");
				if (file.exists())
				{
					assertTrue(file.delete());
				}
				file = new File("testDynamicStore.db.id");
				if (file.exists())
				{
					assertTrue(file.delete());
				}
			}
		}

		public virtual void testClose()
		{
			try
			{
				ByteStore store = ByteStore.createStore("testDynamicStore.db", 30);
				int blockId = store.nextBlockId();
				Collection<DynamicRecord> records = store.allocateRecords(blockId, new sbyte[10]);
				foreach (DynamicRecord record in records)
				{
					store.updateRecord(record);
				}
				store.Close();
//            
//             * try { store.allocateRecords( blockId, new byte[10] ); fail(
//             * "Closed store should throw exception" ); } catch (
//             * RuntimeException e ) { // good }
//             
				try
				{
					store.getBytes(0);
					fail("Closed store should throw exception");
				}
				catch (RuntimeException e)
				{ // good
				}
				try
				{
					store.getLightRecords(0);
					fail("Closed store should throw exception");
				}
				catch (RuntimeException e)
				{ // good
				}
			}
			finally
			{
				File file = new File("testDynamicStore.db");
				if (file.exists())
				{
					assertTrue(file.delete());
				}
				file = new File("testDynamicStore.db.id");
				if (file.exists())
				{
					assertTrue(file.delete());
				}
			}
		}

		public virtual void testStoreGetCharsFromString()
		{
			try
			{
				const string STR = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
				ByteStore store = ByteStore.createStore("testDynamicStore.db", 30);
				int blockId = store.nextBlockId();
				char[] chars = new char[STR.Length];
				STR.getChars(0, STR.Length, chars, 0);
				Collection<DynamicRecord> records = store.allocateRecords(blockId, chars);
				foreach (DynamicRecord record in records)
				{
					store.updateRecord(record);
				}
			// assertEquals( STR, new String( store.getChars( blockId ) ) );
				store.Close();
			}
			finally
			{
				File file = new File("testDynamicStore.db");
				if (file.exists())
				{
					assertTrue(file.delete());
				}
				file = new File("testDynamicStore.db.id");
				if (file.exists())
				{
					assertTrue(file.delete());
				}
			}
		}

		public virtual void testRandomTest()
		{
			Random random = new Random(System.currentTimeMillis());
			ByteStore store = ByteStore.createStore("testDynamicStore.db", 30);
			List<int> idsTaken = new List<int>();
			java.util.Map<int, sbyte[]> byteData = new Dictionary<int, sbyte[]>();
			float deleteIndex = 0.2f;
			float closeIndex = 0.1f;
			int currentCount = 0;
			int maxCount = 128;
			HashSet<int> @set = new HashSet<int>();
			try
			{
				while (currentCount < maxCount)
				{
					float rIndex = random.nextFloat();
					if (rIndex < deleteIndex && currentCount > 0)
					{
						int blockId = idsTaken.Remove(random.Next(currentCount)).intValue();
						store.getLightRecords(blockId);
						validateData(store.getBytes(blockId), byteData.Remove(new int(blockId)));
						Collection<DynamicRecord> records = store.getLightRecords(blockId);
						foreach (DynamicRecord record in records)
						{
							record.setInUse(false);
							store.updateRecord(record);
							@set.Remove(record.getId());
						}
						currentCount--;
					}
					else
					{
						sbyte[] bytes = createRandomBytes(random);
						int blockId = store.nextBlockId();
						Collection<DynamicRecord> records = store.allocateRecords(blockId, bytes);
						foreach (DynamicRecord record in records)
						{
							Debug.Assert(!@set.Contains(record.getId()));
							store.updateRecord(record);
							@set.Add(record.getId());
						}
						idsTaken.Add(new int(blockId));
						byteData.put(new int(blockId), bytes);
						currentCount++;
					}
					if (rIndex > (1.0f - closeIndex) || rIndex < closeIndex)
					{
						store.Close();
						store = new ByteStore("testDynamicStore.db");
					}
				}
			}
			catch (Exception e)
			{
				e.printStackTrace();
				fail("" + e);
			}
			finally
			{
				store.Close();
				File file = new File("testDynamicStore.db");
				if (file.exists())
				{
					assertTrue(file.delete());
				}
				file = new File("testDynamicStore.db.id");
				if (file.exists())
				{
					assertTrue(file.delete());
				}
			}
		}

		private class ByteStore : AbstractDynamicStore
		{
		// store version, each store ends with this string (byte encoded)
			private const string VERSION = "DynamicTestVersion v0.1";

			public ByteStore(string fileName) : base(fileName)
			{
			}

			public virtual string getTypeAndVersionDescriptor()
			{
				return VERSION;
			}

			public static ByteStore createStore(string fileName, int blockSize)
			{
				createEmptyStore(fileName, blockSize, VERSION);
				return new ByteStore(fileName);
			}

			public virtual sbyte[] getBytes(int blockId)
			{
				return get(blockId);
			}

		// public char[] getChars( int blockId ) throws IOException
		// {
		// return getAsChar( blockId );
		// }

			public virtual void flush()
			{
			}
		}

		private sbyte[] createRandomBytes(Random r)
		{
			return new sbyte[r.Next(1024)];
		}

		private void validateData(sbyte[] data1, sbyte[] data2)
		{
			assertEquals(data1.Length, data2.Length);
			for (int i = 0; i < data1.Length; i++)
			{
				assertEquals(data1[i], data2[i]);
			}
		}
	}
}